package com.lfy.modules.safety.filters;

import cn.hutool.crypto.digest.DigestUtil;

import com.lfy.modules.common.domain.R;
import com.lfy.modules.common.utils.HttpDataUtil;
import com.lfy.modules.common.utils.JsonUtils;
import com.lfy.modules.common.utils.RequestUtil;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.IOException;
import java.util.Objects;
import java.util.SortedMap;

/**
 * 上线最后一步：接口签名
 * 签名算法：请求参数转json字符串+secret（随机8位字符串），然后md5加密，得到签名字符串。
 *
 * @author <a href="https://www.lvfeiy.top">lvfeiyang</a>
 * @date 2024/6/30 17:28
 */
@Slf4j
public class SignFilter implements Filter {
    private static final String FILTER = "POST";
    private static final String SIGN_HEADER = "X-Sign";
    private static final String SIGN_TIME = "X-Timestamp";
    private static final String SIGN_SECRET = "X-Secret";
    private static final Long OUT_TIME = 1000 * 60L;

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        // 校验请求参数签名
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        if(!request.getMethod().equals(FILTER)){
            filterChain.doFilter(request, servletResponse);
            return;
        }
        // 判断是否为文件上传请求
        if (request.getContentType() != null && request.getContentType().startsWith("multipart/form-data")) {
            // 是文件上传请求，直接传递给下一个过滤器或目标资源，不执行签名验证逻辑
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        }
        //保存流
        HttpServletRequestWrapper requestWrapper = new SignRequestWrapper(request);
        String sign = request.getHeader(SIGN_HEADER);
        String time = request.getHeader(SIGN_TIME);
        String secret = request.getHeader(SIGN_SECRET);
        if (Objects.isNull(sign) || Objects.isNull(time) || Objects.isNull(secret)) {
            RequestUtil.writeResponse(R.error("呵呵"), servletResponse);
            log.error("签名参数缺失");
            return;
        }
        //防重放
        if (System.currentTimeMillis() - Long.parseLong(time) > OUT_TIME) {
            RequestUtil.writeResponse(R.error("请求超时"), servletResponse);
            log.error("请求超时");
            return;
        }

        boolean accept = true;
        SortedMap<String, String> paramMap = null;
        if (FILTER.equals(request.getMethod())) {
            paramMap = HttpDataUtil.getBodyParams(requestWrapper);
            accept = extracted(paramMap, sign, secret);
        }
        if (!accept) {
            RequestUtil.writeResponse(R.error("签名错误"), servletResponse);
            log.info("sign:{},time:{},secret:{}", sign, time, secret);
            if(Objects.nonNull(paramMap)){
                log.info(JsonUtils.toStr(paramMap));
            }
            log.error("签名错误");
            return;
        }

        filterChain.doFilter(requestWrapper, servletResponse);
    }

    private boolean extracted( SortedMap<String, String> parameterMap, String sign, String secret) {
        String md5Hex = null;
        if(Objects.isNull(parameterMap) || parameterMap.isEmpty()){
            md5Hex = DigestUtil.md5Hex(secret);
        }else {
            md5Hex = DigestUtil.md5Hex((JsonUtils.toStr(parameterMap).concat(secret)));
        }

        return sign.equals(md5Hex);

    }
}
